/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jpxx.workflow.rule;

import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

/**
 * @author John Lee
 * 
 */
public abstract class Rule<T> implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 8600510369643622870L;
	protected String left;
	protected T right;
	protected Operator operator;

	/**
	 * @return the left
	 */
	public final String getLeft() {
		return left;
	}

	/**
	 * @param left
	 *            the left to set
	 */
	public final void setLeft(String left) {
		this.left = left;
	}

	/**
	 * @return the right
	 */
	public final T getRight() {
		return right;
	}

	/**
	 * @param right
	 *            the right to set
	 */
	public final void setRight(T right) {
		this.right = right;
	}

	/**
	 * @return the operator
	 */
	public final Operator getOperator() {
		return operator;
	}

	/**
	 * @param operator
	 *            the operator to set
	 */
	public final void setOperator(Operator operator) {
		this.operator = operator;
	}

	/**
	 * 
	 * @param ruleExecutor
	 * @return
	 */
	public abstract boolean result(RuleExecutor ruleExecutor);

	@Override
	public String toString() {
		return "[" + left + " " + operator.getOperator() + " " + right + "]";
	}

	@SuppressWarnings("rawtypes")
	public static Rule parseRule(String rule) {
		if (StringUtils.isBlank(rule)) {
			return null;
		}
		String regex = "^[\\[][\\w\\.\\s]+(<|<=|==|!=|>=|>)[\\w\\s]+[\\]]$";
		Pattern pattern = Pattern.compile(regex);
		Matcher m = pattern.matcher(rule);
		if (m.matches()) {
			// Ignore "[" and "]"
			rule = rule.substring(1, rule.length() - 1);

			regex = "(<|<=|==|!=|>=|>)";
			pattern = Pattern.compile(regex);
			m = pattern.matcher(rule);

			String operator = null;
			String left = null;
			String right = null;
			if (m.find()) {
				left = rule.substring(0, m.start()).trim();
				operator = rule.substring(m.start(), m.end());
				right = rule.substring(m.end()).trim();
			} else {
				return null;
			}

			// Boolean
			if (right.equalsIgnoreCase("true")
					|| right.equalsIgnoreCase("false")) {
				BooleanRule brule = new BooleanRule();
				brule.setLeft(left);
				brule.setRight(Boolean.valueOf(right));
				brule.setOperator(Operator.getOperator(operator));
				return brule;
			}

			//
			regex = "^[1-9]\\d*$";
			pattern = Pattern.compile(regex);
			m = pattern.matcher(right);
			if (m.find()) {
				LongRule irule = new LongRule();
				irule.setLeft(left);
				irule.setRight(Long.valueOf(right));
				irule.setOperator(Operator.getOperator(operator));
				return irule;
			} else {
				StringRule srule = new StringRule();
				srule.setLeft(left);
				srule.setRight(right);
				srule.setOperator(Operator.getOperator(operator));
				return srule;
			}
		}
		return null;
	}
}